﻿using System;
using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Reflection;
using System.Security.Principal;
using System.Threading;
using System.Xml;
using Baggr.FeedParser;
using log4net;
using Wintellect.Threading.AsyncProgModel;

namespace Baggr.Agent
{
    public sealed class LoadFeed
    {
        private readonly Guid _FeedId;
        private readonly Uri _Url;
        private static readonly ILog Log = LogManager.GetLogger(typeof (LoadFeed));
        private WebRequest _Request;
        private IFeed _ResultFeed;
        private ManualResetEvent _Event = new ManualResetEvent(false);
        private Exception _Exception;
        private AsyncCallback _Callback;
        private IAsyncResult _AsyncResult;

        public LoadFeed(Guid feedId, Uri url)
        {
            if (url == null)
                throw new ArgumentNullException("url");
            _FeedId = feedId;
            _Url = url;
        }

        public IAsyncResult BeginLoad(AsyncCallback callback, object state)
        {
            if (_AsyncResult != null)
                throw new ArgumentException("Opperation is started");
            _Callback = callback;
            _AsyncResult = new AsyncResult(callback, state);
            _Request = WebRequest.Create(_Url);
            _Request.AuthenticationLevel = AuthenticationLevel.None;
            _Request.ImpersonationLevel = TokenImpersonationLevel.Anonymous;
            _Request.BeginGetResponse(OnLoaded, null);
            Log.InfoFormat("Begin: {0}", _FeedId);
            return _AsyncResult;
        }

        public IFeed EndLoad(IAsyncResult result)
        {
            if(result == null)
                throw new ArgumentException("Result isn't corresponding");

            if (_Exception != null)
                throw _Exception;

            _Event.WaitOne();
            Debug.Assert(_ResultFeed != null);
            return _ResultFeed;
        }
        private void OnLoaded(IAsyncResult result)
        {
            Debug.Assert(result.IsCompleted, "result.IsCompleted");
            try
            {
                using (var response = _Request.EndGetResponse(result))
                {
                    using (var responseStream = response.GetResponseStream())
                    {
                        var settings = new XmlReaderSettings {DtdProcessing = DtdProcessing.Ignore};
                        using(var reader = XmlReader.Create(responseStream, settings))
                        {
                            _ResultFeed = reader.GetFeed();
                        }
                    }
                }
            }
            catch(Exception e)
            {
                _Exception = e;
            }

            Log.InfoFormat("End: {0}", _FeedId);
            _Event.Set();

            if (_Callback != null)
                _Callback(_AsyncResult);
        }
    }
}
